package ru.spbu.math.seabattle.client.jms;

import java.awt.event.WindowListener;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import ru.spbu.math.seabattle.client.IClient;
import ru.spbu.math.seabattle.client.jms.gui.GameEndedGUI;
import ru.spbu.math.seabattle.client.jms.gui.GameRequestedGUI;
import ru.spbu.math.seabattle.client.jms.gui.PlayerListGUI;
import ru.spbu.math.seabattle.client.jms.gui.SeaBattleGUI;
import ru.spbu.math.seabattle.client.jms.gui.WaitingForReplyGUI;

import ru.spbu.math.seabattle.entity.Action;
import ru.spbu.math.seabattle.entity.FieldModel;
import ru.spbu.math.seabattle.entity.GameResult;
import ru.spbu.math.seabattle.entity.PlayerID;
import ru.spbu.math.seabattle.entity.TurnResult;

import ru.spbu.math.seabattle.server.IServer;

public class SeaBattleClient implements IClient {

	private static final long serialVersionUID = 1L;
	
	private final ConcurrentMap<PlayerID, String> playerNames = new ConcurrentHashMap<PlayerID, String>();
	
	private PlayerID myID; 
	private String myName;
	
	private SeaBattleGUI seaBattleGui;
	private PlayerListGUI playerList;
	private GameEndedGUI endDialog;
	private GameRequestedGUI gameRequestGui;
	private WaitingForReplyGUI waitingForReplyGui;
	
	private IServer server;
	
	public void init(IServer server, String name) {
		this.server = server;
		server.hello(this, name);
	}
	
	public void sayGoodbyeToYourServer() {
		server.goodbye(myID);
	}
	// Invoked when this client shoots somewhere.
	public void onShot(Action action) {
		server.makeTurn(myID, action);
	}
	
	public String getName() {
		return myName;
	}
		
	// Invoked when player finishes placing his ships.
	public void onShipPlaced(FieldModel model) {
		server.setField(myID, model);
	}
	
	// Invoked when this client selects a player
	public void onPlayerSelected(PlayerID id) {
		server.requestGame(myID, id);
		waitingForReplyGui = new WaitingForReplyGUI(this, playerNames.get(id), id, playerList.getX(), playerList.getY());
		waitingForReplyGui.setAlwaysOnTop(true);
		playerList.setEnabled(false); 
	}
	
	// Invoked when this client firstly selects a player and then cancels selection
	public void onPlayerSelectionCanceled(PlayerID id) {
		server.withdrawGame(myID, id);
		waitingForReplyGui.setVisible(false);
		waitingForReplyGui = null;
		playerList.setEnabled(true);
	}
	
	// Invoked when this client puts his personal bid into player list.
	public void onApplicationPut() {
		server.requestGame(myID);
	}
	
	// Invoked when this client withdraws his personal bid from player list
	public void onApplicationWithdrawn() {
		server.withdrawGame(myID);
	}
	
	// Invoked when this client firstly puts his personal bid to player list, 
	// then someone selected this client, and then this client accepted this someone's request
	// The game begins after this method is invoked.
	public void onGameRequestAccepted(PlayerID opponentID) {
		server.acceptGameRequest(myID, opponentID);
		gameRequestGui.setVisible(false);
		gameRequestGui = null;
	}
	
	// Invoked when this client firstly puts his personal bid to player list, 
	// then someone selected this client, and then this client declines this someone's request
	public void onGameRequestDeclined(PlayerID opponentID) {
		server.refuseGameRequest(myID, opponentID);
		gameRequestGui.setVisible(false);
		gameRequestGui = null;
		playerList.setEnabled(true);
	}
	
	@Override
	public void welcome(PlayerID givenID, String givenName) {
		
		System.out.println("CLIENT: Connected to server.");
		myID = givenID;
		myName = givenName;
		playerList = new PlayerListGUI(this);
	}
	
	@Override
	public void onGameStarted() {

		playerList.setVisible(false);
		playerList = null;
		if (waitingForReplyGui != null) {
			waitingForReplyGui.setVisible(false);
			waitingForReplyGui = null;
		}
		seaBattleGui = new SeaBattleGUI(this, myName);
		seaBattleGui.setVisible(true);
		seaBattleGui.startPlacingShips();
	}
	
	@Override
	public void onEnemyTurnEnded(TurnResult result) {
		
		seaBattleGui.shootPlayer(result);
	}

	@Override
	public void onEnemyTurnStarted() {
		
		seaBattleGui.waitForTurn();
	}

	@Override
	public void onGameEnded(GameResult result) {
		
		endDialog = new GameEndedGUI(this, result);
	}

	// Invoked, when someone selects this client's bid 
	@Override
	public void onGameRequest(PlayerID opponentID, String opponentName) {

		gameRequestGui = new GameRequestedGUI(this, opponentName, opponentID, playerList.getX(), playerList.getY());
		gameRequestGui.setAlwaysOnTop(true);
		playerList.setEnabled(false);
	}

	// Invoked when this client selects a player, and that player refuses to play.
	@Override
	public void onGameRequestRefused(PlayerID opponentID) {
		System.out.println(myName + " is a loser");
		waitingForReplyGui.setVisible(false);
		waitingForReplyGui= null;
		playerList.setEnabled(true);
	}

	@Override
	public void onPlayerJoined(PlayerID id, String name) {

		System.out.println("CLIENT (" + myName + "): Player joined " + name);
		playerList.onPlayerJoined(id, name);
	}

	@Override
	public void onPlayerLeft(PlayerID id) {

		playerList.onPlayerLeft(id);
	}

	@Override
	public void onYourTurnEnded(TurnResult result) {
		
		seaBattleGui.waitForTurn();
		seaBattleGui.shootEnemy(result);
	}

	@Override
	public void onYourTurnStarted() {
		
		seaBattleGui.startTurn();
	}

	@Override
	public void onPlayerAddedOrRemoved(boolean added, PlayerID id, String name) {
		
		if (added)
			playerNames.put(id, name);
		else 
			playerNames.remove(id);
	}
	
	// Invoked when someone firstly selects this clients bid, and then withdraws it.
	@Override
	public void onGameRequestWithdrawn(PlayerID opponentID) {
		gameRequestGui.setVisible(false);
		gameRequestGui = null;
		playerList.setEnabled(true);
	}
	
	public void continueGame() {
		for (WindowListener l : endDialog.getWindowListeners()) 
			endDialog.removeWindowListener(l);
		
		endDialog.dispose();
		seaBattleGui.dispose();		
		playerList = new PlayerListGUI(this);
	}
	
	public void exitGame() {
		
		server.goodbye(myID);
		
		for (WindowListener l : endDialog.getWindowListeners()) 
			endDialog.removeWindowListener(l);
		
		if (seaBattleGui != null)
			seaBattleGui.dispose();
		if (endDialog != null) 
			endDialog.dispose();
	}
	
	
	public SeaBattleGUI getSeaBattleGui() {
		return seaBattleGui;
	}

	public PlayerID getPlayerID() {
		return myID;
	}
}
